Repository navigation
Add cross-host workspace lease for mutating Workbench runs - #99
Merged
Merged
Conversation
ADR 0004's "one mutating run per workspace" invariant is enforced only in-memory per process (WorkbenchProcessScheduler.mutationLocked, WorkbenchRunJournal.writeQueue). Two hosts (VS Code extension + standalone server, both legitimate per ADR 0001 decision 2) opened on the same workspace can each believe they are the sole mutator, causing lost journal updates. This adds ADR 0010 (advisory lease, v1: refuse-immediately, no CAS/observer mode for now) plus the OpenSpec change proposal/design/tasks/ specs. Bundles the wire-contract COMMAND_KINDS duplication fix (wire.ts vs protocol.ts) into the same change since it touches the same files. No implementation yet — pausing here per plan for design review before writing the lease/scheduler code itself. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
WorkbenchProcessScheduler's mutation lock and WorkbenchRunJournal's write serialization are both private, per-process state. A VS Code extension and a standalone server can legitimately be open on the same workspace at once (ADR 0001 decision 2), each believing it is the sole mutator, causing lost journal updates. Adds a versioned, file-based workspace lease (write-then-rename, same pattern as the journal): a mutating run acquires it, renews it while active, and releases it on completion; a competing host is refused immediately with a message naming the current holder, and a stale lease (crashed holder) is reclaimed and disclosed. Also fixes wire.ts's COMMAND_KINDS duplicating core's CommandKind list (same files this change already touches), and closes a larger pre-existing gap found while implementing this: the standalone server's own `implement` execution over WebSocket never went through the scheduler at all, so ADR 0004's mutation isolation was unenforced there even same-host. Both are now scheduler-gated the same way. Adds workspace-lease.ts + tests, extends process-scheduler.ts (including a fix so `completion` only resolves after lease release, not before, per a real race the tests caught), wires the lease into WorkbenchRecoveryService and the extension's activate(), and routes the server's implement command through WorkbenchRecoveryService.runMutating() (no checkpoint capture yet — mutation exclusivity only, a scoped-down first pass; see design.md Non-Goals). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2 tasks done
VeryComplexAndLongName
added a commit
that referenced
this pull request
Aug 28, 2026
Merged in #99; task 7.3 (a fully manual VS Code + standalone smoke test) stayed open (no interactive VS Code UI in that environment) but every spec.md scenario has real test coverage, satisfying the archive gate in openspec/config.yaml. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
3 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
wire.ts'sCOMMAND_KINDSduplicating core'sCommandKindlist.implementexecution over WebSocket never went throughWorkbenchProcessSchedulerat all, so ADR 0004's same-host mutation isolation was unenforced there. This PR closes that too —websocket.ts'simplementpath is now routed throughWorkbenchRecoveryService.runMutating()(mutation lock + lease; no checkpoint capture yet, deliberately out of scope — see design.md Non-Goals).@openspec-ui/core0.29.0→0.30.0,@openspec-ui/server1.10.0→1.11.0,openspec-ui-vscode0.26.0→0.27.0 (all minor).Test plan
npm run typecheckworkspace-widenpm run lint(includinglint:english) workspace-widenpm run testworkspace-wide — newworkspace-lease.test.ts, extendedprocess-scheduler.test.ts(lease-gated scheduling), newwire.test.ts, and a new real two-server-process WebSocket test inserver.test.tsproving the lease actually blocks a competing host'simplementattempt and unblocks after the first finishesopenspec change validate --strict cross-host-workspace-leasetasks.md7.3.🤖 Generated with Claude Code